package com.fc.happyteam.shiro.config;

import com.fc.happyteam.config.BasicConfiguration;
import com.fc.happyteam.service.IUserPermService;
import com.fc.happyteam.service.IUserRoleService;
import com.fc.happyteam.service.IUserService;
import com.fc.happyteam.shiro.common.HtSessionFactory;
import com.fc.happyteam.shiro.common.MyDefaultWebSessionManager;
import com.fc.happyteam.shiro.common.RetryLimitHashedCredentialsMatcher;
import com.fc.happyteam.shiro.common.SessionDao;
import com.fc.happyteam.shiro.filter.KickoutSessionFilter;
import com.fc.happyteam.shiro.filter.MyFormAuthenticationFilter;
import com.fc.happyteam.shiro.filter.MyLogoutFilter;
import com.fc.happyteam.shiro.listener.KickOutSessionListener;
import com.fc.happyteam.shiro.properties.ShiroProps;
import com.fc.happyteam.shiro.properties.ShiroSessionProps;
import com.fc.happyteam.shiro.realm.UserRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.WebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Import;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@Configuration
@EnableConfigurationProperties({ShiroSessionProps.class, ShiroProps.class})
@Import(BasicConfiguration.class)
public class ShiroConfiguration
{
    @Autowired
    private ShiroProps shiroProps;

    @Autowired
    private ShiroSessionProps shiroSessionProps;

    @Bean(name = "credentialsMatcher")
    @DependsOn("cacheManager")
    public CredentialsMatcher credentialsMatcher(CacheManager cacheManager)
    {
        RetryLimitHashedCredentialsMatcher credentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager);
        credentialsMatcher.setHashAlgorithmName(shiroProps.getHashAlgorithmName());
        credentialsMatcher.setHashIterations(shiroProps.getHashIterations());
        credentialsMatcher.setStoredCredentialsHexEncoded(shiroProps.isStoredCredentialsHexEncoded());
        credentialsMatcher.setRetryMax(shiroProps.getRetryMax());
        return credentialsMatcher;
    }

    @Bean(name = "userRealm")
    @DependsOn({"userService","userRoleService","userPermService","credentialsMatcher"})
    @SuppressWarnings("SpringJavaAutowiringInspection")
    public Realm userRealm(IUserService userService, IUserRoleService userRoleService, IUserPermService userPermService, CredentialsMatcher credentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        userRealm.setUserService(userService);
        userRealm.setUserRoleService(userRoleService);
        userRealm.setUserPermService(userPermService);
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }

    @Bean(name = "cacheManager")
    @ConfigurationProperties(prefix="shiro")
    public CacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ShiroProps.Ehcache ehcache = shiroProps.getEhcache();
        if (ehcache.getCacheManagerConfigFile() != null) {
            ehCacheManager.setCacheManagerConfigFile(ehcache.getCacheManagerConfigFile());
        }
        return ehCacheManager;
    }

    @Bean
    public SessionDAO sessionDAO(CacheManager cacheManager) {
        SessionDao sessionDAO = new SessionDao();
        sessionDAO.setActiveSessionsCacheName(shiroSessionProps.getActiveSessionsCacheName());
        Class<? extends SessionIdGenerator> idGenerator = shiroSessionProps.getIdGenerator();
        if (idGenerator != null) {
            SessionIdGenerator sessionIdGenerator = BeanUtils.instantiateClass(idGenerator);
            sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        }
        sessionDAO.setCacheManager(cacheManager);
        return sessionDAO;
    }

    @Bean
    @DependsOn(value = {"cacheManager", "sessionDAO"})
    public WebSessionManager sessionManager(CacheManager cacheManager, SessionDAO sessionDAO) {
        MyDefaultWebSessionManager sessionManager = new MyDefaultWebSessionManager();
        sessionManager.setCacheManager(cacheManager);
        sessionManager.setGlobalSessionTimeout(shiroSessionProps.getGlobalSessionTimeout());
        sessionManager.setSessionFactory(new HtSessionFactory());
        sessionManager.setSessionDAO(sessionDAO);

        //session listeners
        List<SessionListener> listeners = new ArrayList<SessionListener>();

        KickOutSessionListener kickOutSessionListener = new KickOutSessionListener();
        kickOutSessionListener.setCacheManager(cacheManager);
        listeners.add(kickOutSessionListener);
        sessionManager.setSessionListeners(listeners);
        return sessionManager;
    }

    @Bean(name = "securityManager")
    @DependsOn("userRealm")
    public SecurityManager securityManager(Realm realm, CacheManager cacheManager, SessionManager sessionManager) {
        DefaultSecurityManager sm = new DefaultWebSecurityManager();
        sm.setRealm(realm);
        sm.setCacheManager(cacheManager);
        sm.setSessionManager(sessionManager);
        //sm.setRememberMeManager(rememberMeManager);
        return sm;
    }

    @Bean
    @DependsOn("securityManager")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
                                                         CacheManager cacheManager, SessionManager sessionManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
        MyLogoutFilter logoutFilter = new MyLogoutFilter();
        logoutFilter.setRedirectUrl("/login");
        filters.put("logout", logoutFilter);

        KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();
        kickoutSessionFilter.setCacheManager(cacheManager);
        kickoutSessionFilter.setSessionManager(sessionManager);

        filters.put("kickout", kickoutSessionFilter);

        MyFormAuthenticationFilter formAuthenticationFilter = new MyFormAuthenticationFilter();
        filters.put("authc", formAuthenticationFilter);


        Map<String, Filter> filterClasses = instantiateFilterClasses(shiroProps.getFilters());
        if (filterClasses != null) {
            filters.putAll(filterClasses);
        }

        shiroFilterFactoryBean.setFilters(filters);

        Map<String, String> filterChainDefinitionManager = new LinkedHashMap<String, String>();

        if (shiroProps.getFilterChainDefinitions() != null) {
            filterChainDefinitionManager.putAll(shiroProps.getFilterChainDefinitions());
        }

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionManager);

        shiroFilterFactoryBean.setLoginUrl(shiroProps.getLoginUrl());
        shiroFilterFactoryBean.setSuccessUrl(shiroProps.getSuccessUrl());
        shiroFilterFactoryBean.setUnauthorizedUrl(shiroProps.getUnauthorizedUrl());

        return shiroFilterFactoryBean;
    }

    @Bean(name = "shiroFilter")
    @DependsOn("securityManager")
    public FilterRegistrationBean filterRegistrationBean(SecurityManager securityManager, CacheManager cacheManager, SessionManager sessionManager) throws Exception {
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setFilter((Filter) shiroFilterFactoryBean(securityManager,cacheManager,sessionManager).getObject());
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
        daap.setProxyTargetClass(true);
        return daap;
    }

    private Map<String, Filter> instantiateFilterClasses(Map<String, Class<? extends Filter>> filters) {
        Map<String, Filter> filterMap = null;
        if (filters != null) {
            filterMap = new LinkedHashMap<String, Filter>();
            for (String name : filters.keySet()) {
                Class<? extends Filter> clazz = filters.get(name);
                Filter f = BeanUtils.instantiate(clazz);
                filterMap.put(name, f);
            }
        }
        return filterMap;
    }
}
